package com.yihaodian.index.solr;

import java.io.File;
import java.io.IOException;
import java.util.Map;

import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;

import org.apache.log4j.Logger;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.search.spell.HighFrequencyDictionary;
import org.apache.lucene.search.spell.TermFreqIterator;
import org.apache.lucene.search.suggest.analyzing.AnalyzingSuggester;
import org.apache.lucene.search.suggest.analyzing.FuzzySuggester;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.util.Version;
import org.apache.solr.core.CoreContainer;
import org.apache.solr.core.CoreDescriptor;
import org.apache.solr.core.SolrConfig;
import org.apache.solr.core.SolrCore;
import org.apache.solr.schema.IndexSchema;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;

import com.google.common.collect.Maps;
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.yihaodian.api.common.Configs;
import com.yihaodian.api.search.SearchException;
import com.yihaodian.api.search.SearchService;
import com.yihaodian.cluster.Node;
import com.yihaodian.cluster.discovery.DiscoveryModule;
import com.yihaodian.cluster.discovery.zk.ZkPath;
import com.yihaodian.index.IndexShard;
import com.yihaodian.index.Indexer;
import com.yihaodian.index.meta.shard.ShardId;
import com.yihaodian.index.solr.ext.MemDataManager;
import com.yihaodian.search.core.Searcher;
import com.yihaodian.search.core.SuggestHandler;
import com.yihaodian.search.core.Suggester;
import com.yihaodian.server.SearchServiceExporter;
import com.yihaodian.zk.BlockingWriteLock;
import com.yihaodian.zk.ZkClient;

/**
 * DirectSolrConnection
 * 
 */
public class SolrIndexShard implements IndexShard {
	static Logger logger = Logger.getLogger(SolrIndexShard.class);

	Node node = null;

	CoreContainer coreContainer;

	IndexSchema indexSchema;

	SolrConfig solrConfig;

	SolrCore solrCore;

	ShardId shardId;

	String indexDir;

	SolrSearcher searcher = null;

	SolrIndexer indexer = null;
	
	ZkClient zkClient;

	Map<String, AnalyzingSuggester> suggesterMap = Maps.newHashMap();

	public SolrIndexShard(Node localNode, ShardId shard, String indexDir,
			IndexSchema schema, SolrConfig solrConfig,
			CoreContainer coreContainer) {
		this.node = localNode;
		this.shardId = shard;
		this.indexDir = indexDir;
		this.indexSchema = schema;
		this.solrConfig = solrConfig;
		this.coreContainer = coreContainer;
		
//		ApplicationContext app = new ClassPathXmlApplicationContext(
//        "spring.xml");
//		SearchServiceExporter searchService = (SearchServiceExporter)app.getBean("searchService");
//		zkClient = searchService.getNodeBootstrap().getInjector().getInstance(ZkClient.class);
	}
	
	@Override
	public void startup() throws SearchException {
		String singleNode = Configs.get("one.node.per.jvm", "false");
		boolean isOneNode = Boolean.parseBoolean(singleNode);
		String id = shardId.getUUId();

		if (!isOneNode)
			id = node.getId().replace(":", "_") + "_" + id;

		CoreDescriptor descriptor = new CoreDescriptor(coreContainer, id,
				indexDir);
		this.solrCore = new SolrCore(id, indexDir, solrConfig, indexSchema,
				descriptor);
		/**
		 * CoreContainer register SolrCore, add to CoreMaps
		 */
		this.coreContainer.register(this.solrCore, false);
		this.searcher = new SolrSearcher(solrCore);
		this.indexer = new SolrIndexer(solrCore);
	}

	@Override
	public void shutdown() throws SearchException {
		this.solrCore.close();

		MemDataManager.unRegisterDataDir(indexDir);
	}

	@Override
	public Searcher getSearcher() {
		return searcher;
	}

	@Override
	public Indexer getIndexer() throws IOException {
		return indexer;
	}

	@Override
	public void switchIndex(String version) {
		indexDir = indexDir.substring(0, indexDir.lastIndexOf('/') + 1) + version;
		logger.info("switch index: " + version);
		logger.info("index dir: " + indexDir);
		// TODO guice
		String lockRoot = "/search/clusters/cluster-zw/lock";
		BlockingWriteLock lock = zkClient.getWriteLock(
				lockRoot + "/" + shardId.getIndexId() + "/switchLock");
		try{
			lock.lock();
			startup();
		} catch (Exception e) {
			// TODO: handle exception
		} finally {
			if (lock != null) {
				lock.unlock();
			}
		}
	}

	@Override
	public Suggester getSuggester(String field) throws IOException {
		AnalyzingSuggester suggester = suggesterMap.get(field);

		if (suggester == null) {
			suggester = makeAnalyzingSuggester(field);
		}

		SuggestHandler handler = new SuggestHandler(suggester);
		return handler;
	}

	/**
	 * TODO refresh the dict.
	 * 
	 * @param field
	 * @return
	 */
	synchronized AnalyzingSuggester makeAnalyzingSuggester(String field)
			throws IOException {
		AnalyzingSuggester suggester = suggesterMap.get(field);
		if (suggester != null)
			return suggester;
		

		String idx = solrCore.getIndexDir();
		logger.info("create AnalyzingSuggester for " + idx + " " + field);
		File path = new File(idx);
		Directory directory = FSDirectory.open(path);

		IndexReader sourceReader = DirectoryReader.open(directory);

		HighFrequencyDictionary dict = new HighFrequencyDictionary(
				sourceReader, field, 0);
		TermFreqIterator tfi = (TermFreqIterator) dict.getWordsIterator();

		suggester = new FuzzySuggester(new StandardAnalyzer(
				Version.LUCENE_43));
		suggester.build(tfi);

		suggesterMap.put(field, suggester);

		return suggester;
	}

	@Inject
	public void setZkClient(ZkClient zkClient) {
		this.zkClient = zkClient;
	}
}
